/**
 * 
 */
package neptune.web.client.view.components.dialogs;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import neptune.web.client.WebInterface;
import neptune.web.client.commands.DeployExperimentCommand;
import neptune.web.client.model.SerializableExperiment;
import neptune.web.client.model.cluster.SerializableCluster;
import neptune.web.client.model.cluster.SerializablePhysicalNode;
import neptune.web.client.model.topology.SerializableNode;
import neptune.web.client.model.topology.SerializableTopology;
import neptune.web.client.services.ClusterService;
import neptune.web.client.services.experiment.ExperimentService;

import com.allen_sauer.gwt.dnd.client.DragContext;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.AbstractDropController;
import com.allen_sauer.gwt.dnd.client.drop.DropController;
import com.allen_sauer.gwt.dnd.client.drop.FlowPanelDropController;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author oltremago
 * 
 */
public class ManualAllocationDialog extends DialogBox {

	private static final String EDITOR_PANEL_HEIGHT = "400px";
	private FlowPanel nodesPanel;
	private FlowPanel machinesPanel;
	private AbsolutePanel absPanel;
	private PickupDragController dragController;
	private ClickListener okListener = new ClickListener() {
		public void onClick(Widget sender) {
			if(nodesPanel.getWidgetCount()>0){
				Window.alert("You must allocate all topology nodes");
				return;
			}
			
			Command command = new DeployExperimentCommand(expId,
					getAllocationMap());
			command.execute();
			hide();
		}
	};
	private ClickListener cancelListener = new ClickListener() {
		public void onClick(Widget sender) {
			hide();
		}
	};
	private AsyncCallback<SerializableExperiment> expInfoCallback = new AsyncCallback<SerializableExperiment>() {

		public void onSuccess(SerializableExperiment result) {
			SerializableTopology topology = result.getTopology();

			ArrayList<SerializableNode> nodes = topology.getNodes();
			String[] nodesId = new String[nodes.size()];
			for (int i = 0; i < nodes.size(); i++) {
				nodesId[i] = nodes.get(i).getId();
			}

			addNodeNames(nodesId);
		}

		public void onFailure(Throwable caught) {
			WebInterface.addMessageToConsoleAndShowDialog(
					"Unable to load experiment infos. Cause: "
							+ caught.getMessage(), true);
		}
	};
	private AsyncCallback<SerializableCluster> clusterInfoCallback = new AsyncCallback<SerializableCluster>() 
	{
		public void onSuccess(SerializableCluster result) 
		{
			ArrayList<SerializablePhysicalNode> nodes = result.getNodes();
			String[] nodeId = new String[nodes.size()];
			String[] nodeName =new String[nodes.size()];
			for (int i = 0; i < nodes.size(); i++) 
			{
				nodeId[i] = nodes.get(i).getId();
				nodeName[i] =nodes.get(i).getName();
			}
			addMachinesPanel(nodeId, nodeName);
		}

		public void onFailure(Throwable caught) 
		{
			WebInterface.addMessageToConsoleAndShowDialog(
					"Unable to load cluster infos. Cause: "
							+ caught.getMessage(), true);
		}
	};
	
	private final String expId;

	public class AllocationMapDropController extends AbstractDropController {

		private FlowPanel realTarget;
		private VerticalPanel panel;

		public AllocationMapDropController(Widget dropTarget) {
			super(dropTarget);
			panel = (VerticalPanel) dropTarget;
			realTarget = (FlowPanel) panel.getWidget(1);
		}

		@Override
		public Widget getDropTarget() {
			return panel;
		}

		@Override
		public void onDrop(DragContext context) {
			realTarget.remove(realTarget.getWidgetCount() - 1);

			if (context.mouseX >= panel.getAbsoluteLeft()
					&& context.mouseX <= panel.getAbsoluteLeft()
							+ panel.getOffsetWidth()) {

				if (context.mouseY >= panel.getAbsoluteTop()
						&& context.mouseY <= panel.getAbsoluteTop()
								+ panel.getOffsetHeight()) {
					for (Object selected : context.selectedWidgets) {
						realTarget.add((Widget) selected);
					}
				} else {
					context.vetoException = new VetoDragException();
				}

			} else {
				context.vetoException = new VetoDragException();
			}

		}

		@Override
		public void onPreviewDrop(DragContext context) throws VetoDragException {
			super.onPreviewDrop(context);
			realTarget.add(new Label("x"));
		}
	}

	public ManualAllocationDialog(String expId) {
		this.expId = expId;
		// Adding nodes
		ClusterService.Util.getInstance().getCluster(clusterInfoCallback);
		ExperimentService.Util.getInstance().getExperiment(expId,
				expInfoCallback);

		// Building interface
		setText("Allocation Map");
		setAnimationEnabled(true);
		setWidth("500px");

		VerticalPanel vPanel = new VerticalPanel();
		vPanel.setHeight("100%");
		vPanel.setWidth("100%");

		absPanel = new AbsolutePanel();
		absPanel.setSize("500px", EDITOR_PANEL_HEIGHT);

		HorizontalPanel editorPanel = new HorizontalPanel();
		editorPanel.setSize("100%", "100%");

		VerticalPanel leftPanel = new VerticalPanel();
		leftPanel.setSize("100%", "100%");
		Label vmLabel = new Label("Topology nodes");
		leftPanel.add(vmLabel);

		VerticalPanel rightPanel = new VerticalPanel();
		rightPanel.setSize("100%", "100%");
		Label pmLabel = new Label("Cluster machines");
		rightPanel.add(pmLabel);

		absPanel.add(editorPanel);

		nodesPanel = new FlowPanel();
		ScrollPanel nodesPanelWrap = new ScrollPanel(nodesPanel);
		leftPanel.add(nodesPanelWrap);
		nodesPanel.setSize("98%", "98%");
		nodesPanelWrap.setSize("100%", EDITOR_PANEL_HEIGHT);
		editorPanel.add(leftPanel);
		editorPanel.setCellWidth(leftPanel, "50%");

		machinesPanel = new FlowPanel();
		ScrollPanel machinesPanelWrap = new ScrollPanel(machinesPanel);
		rightPanel.add(machinesPanelWrap);
		machinesPanel.setSize("98%", "98%");
		machinesPanelWrap.setSize("100%", EDITOR_PANEL_HEIGHT);
		editorPanel.add(rightPanel);
		editorPanel.setCellWidth(rightPanel, "50%");

		vPanel.add(absPanel);

		Label hint = new Label("Press CTRL to select multiple nodes");
		vPanel.add(hint);

		HorizontalPanel buttonsPanel = new HorizontalPanel();
		Button okButton = new Button("Ok");
		okButton.addClickListener(okListener);

		Button cancelButton = new Button("Cancel");
		cancelButton.addClickListener(cancelListener);

		buttonsPanel.add(cancelButton);
		buttonsPanel.add(okButton);
		vPanel.add(buttonsPanel);
		vPanel.setCellHorizontalAlignment(buttonsPanel,
				VerticalPanel.ALIGN_RIGHT);

		setWidget(vPanel);

		// Styles
		addStyleName("AllocationDialog");
		editorPanel.addStyleName("AllocationDialog-editorPanel");
		nodesPanelWrap.addStyleName("AllocationDialog-nodesPanelWrap");
		machinesPanelWrap
				.addStyleName("AllocationDialog-machinesPanelWrap");
		nodesPanel.addStyleName("AllocationDialog-nodesPanel");
		machinesPanel.addStyleName("AllocationDialog-machinesPanel");
		hint.addStyleName("AllocationDialog-hintPanel");
		okButton.addStyleName("AllocationDialog-okButton");
		cancelButton.addStyleName("AllocationDialog-cancelButton");

		// Drag and drop
		dragController = new PickupDragController(absPanel, false);
		dragController.setBehaviorMultipleSelection(true);
		dragController.setBehaviorDragProxy(true);
		DropController nodesDropController = new FlowPanelDropController(
				nodesPanel);
		dragController.registerDropController(nodesDropController);

	}

	private void addNodeNames(String[] names) {
		for (String name : names) {
			Label nameLb = new Label(name);
			nameLb.setWidth("98%");
			nameLb.addStyleName("AllocationDialog-vmName");
			dragController.makeDraggable(nameLb);
			nodesPanel.add(nameLb);
		}
	}

	
	private void addMachinesPanel(String[] idMachines, String[] nameMachines) {
		//for (String machineId : idMachines) 
		for (int i=0 ; i< idMachines.length ; i++)
		{
			String machineId = idMachines[i];
			String machineName = nameMachines[i]; 
			VerticalPanel mainPanel = new VerticalPanel();
			mainPanel.setWidth("100%");

			FlowPanel machinePanel = new FlowPanel();
			machinePanel.setWidth("100%");

			Label title = new Label(machineName);
			title.setTitle(machineId);
			title.addStyleName("AllocationDialog-pmName");
			title.setWidth("98%");

			mainPanel.add(title);
			mainPanel.add(machinePanel);

			machinesPanel.add(mainPanel);

			DropController machineDropController = new AllocationMapDropController( mainPanel );
			dragController.registerDropController(machineDropController);
		}
	}
	
	
	public Map<String, String[]> getAllocationMap()
	{
		Map<String, String[]> allocationMap = new HashMap<String, String[]>(0);

		for (int i = 0; i < machinesPanel.getWidgetCount(); i++) 
		{
			VerticalPanel vPanel = (VerticalPanel) machinesPanel.getWidget(i);
			Label pmLabel = (Label) vPanel.getWidget(0);
			FlowPanel vmsPanel = (FlowPanel) vPanel.getWidget(1);
			
			String[] vms = new String[vmsPanel.getWidgetCount()];
			for (int j = 0; j < vms.length; j++) 
			{
				vms[j] = ((Label) vmsPanel.getWidget(j)).getText();
			}
			//TODO LUIGI settare l'id
			//allocationMap.put(pmLabel.getText(), vms);
			allocationMap.put(pmLabel.getTitle(), vms);
			//Debug
			System.out.println("\n\n");
			System.out.println("pmLabel.getTitle()="+pmLabel.getTitle());
			for (int k=0; k<vms.length; k++)
			{
				System.out.println("vms["+k+"]="+vms[k]);
			}
			System.out.println("\n\n");
			//Debug
		}

		return allocationMap;
	}
}
